package com.money.service.impl;

import com.alibaba.fastjson2.JSON;
import com.api.client.IdClient;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.common.constant.RedisKey;
import com.common.context.UserContext;
import com.common.domain.dto.TimeGolds;
import com.common.domain.po.GoldRecord;
import com.common.domain.po.UserGoldEarnings;
import com.common.domain.po.UserMoney;
import com.common.domain.po.Week;
import com.common.exception.CodeAndMsg;
import com.common.exception.JavaSmException;
import com.common.utils.CollectUtils;
import com.money.mapper.GoldRecordMapper;
import com.money.mapper.UserMoneyMapper;
import com.money.service.GoldRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import springfox.documentation.spring.web.json.Json;

import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 路亚
 * @version 0.1
 * @className GoldRecordServiceImpl
 * @description
 * @date 2024/12/27 16:29
 * @since jdk11
 */
@Service
@RefreshScope
@RequiredArgsConstructor
public class GoldRecordServiceImpl extends ServiceImpl<GoldRecordMapper, GoldRecord> implements GoldRecordService {

    @Value("${user.gold.earnings}")
    private String userGoldEarnings;

    @Value("${viewing.reward}")
    private String viewingReward;

    private final StringRedisTemplate stringRedisTemplate;

    private final IdClient idClient;

    private final UserMoneyMapper userMoneyMapper;

    private final GoldRecordMapper goldRecordMapper;

    private static String GOLD_RECORD_TYPE = "gold_record";

    @Override
    @Transactional
    public void signIn() {
        UserGoldEarnings object = JSON.parseObject(userGoldEarnings, UserGoldEarnings.class);
        String key = RedisKey.SIGN_IN_KEY + UserContext.getCurrentId();
        // 获取当前日期
        LocalDate today = LocalDate.now();
        // 获取当前是星期几
        DayOfWeek dayOfWeek = today.getDayOfWeek();
        Boolean flag = stringRedisTemplate.opsForValue().setBit(key, dayOfWeek.getValue() - 1, true);
        // flag返回的是上次的值，所以为true说明今天已经签到过了
        if (flag){
            throw new JavaSmException(CodeAndMsg.SIGN_IN_ERROR);
        }
        Integer countSignDays = getCountSignDays(key, dayOfWeek.getValue());
        List<Week> weeks = object.getWeeks();
        Week week = weeks.get(dayOfWeek.getValue() - 1);
        Integer golds = week.getGolds();
        if (countSignDays == 3){
            golds = object.getDays3() + golds;
        }else if (countSignDays == 7){
            golds = object.getDays7() + golds;
        }
        GoldRecord goldRecord = new GoldRecord(idClient.getLongKeyInterval(GOLD_RECORD_TYPE), UserContext.getCurrentId(), golds, "签到获得", today);
        goldRecordMapper.insertGoldRecord(goldRecord);
        System.out.println(UserContext.getCurrentId());
        UserMoney userMoney = userMoneyMapper.selectById(UserContext.getCurrentId());
        userMoney.setGoldNum(userMoney.getGoldNum() + golds);
        userMoneyMapper.updateById(userMoney);
    }

    /**
     * 获取连续签到天数
     * 通过Redis的bitField操作获取存储在指定key中的数据，计算连续签到的天数
     *
     * @param key Redis中的键，用于存储签到信息
     * @param len 签到数据的长度，即签到天数的位数表示
     * @return 连续签到的天数，如果没有数据则返回0
     */
    private Integer getCountSignDays(String key, Integer len){
        // 使用Redis的bitField操作获取签到数据
        List<Long> result = stringRedisTemplate.opsForValue()
                .bitField(key, BitFieldSubCommands.create().get(
                        BitFieldSubCommands.BitFieldType.unsigned(len)
                ).valueAt(0));
        // 检查结果是否为空，如果为空则返回0
        if (CollectUtils.isEmpty(result)){
            return 0;
        }
        // 获取结果中的第一个元素，即签到数据
        Integer num = result.get(0).intValue();
        // 初始化连续签到天数计数器
        Integer count = 0;
        // 循环检查签到数据的每一位，计算连续签到天数
        while ((num & 1) == 1){
            count++;
            // 逻辑右移操作，检查下一位
            num >>>= 1;
        }
        // 返回连续签到天数
        return count;
    }

    @Override
    @Transactional
    public void viewingTask() {
        // 整体实现思路
        // 前端每5秒发送一次请求，后端统计当前用户观看了多久（在redis中统计
        // 达到一定时间后，自动给用户奖励金币
        // 1.判断当前用户是否已经完成了任务，如果完成了则返回
        String isDone = stringRedisTemplate.opsForValue().get(RedisKey.VIEWING_IS_DONE_KEY + UserContext.getCurrentId());
        if ("true".equals(isDone)){
            throw new JavaSmException(CodeAndMsg.VIEWING_IS_DONE);
        }
        // 2.开始统计时间
        String key = RedisKey.VIEWING_COUNT_TIME_KEY + UserContext.getCurrentId();
        Long increment = stringRedisTemplate.opsForValue().increment(key, 5);
        // 3.每次观看5秒，判断是否达到一定时间
        List list = JSON.parseObject(viewingReward, List.class);
        List<TimeGolds> timeGoldsList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            TimeGolds timeGolds = JSON.parseObject(list.get(i).toString(), TimeGolds.class);
            timeGoldsList.add(timeGolds);
        }
        String phaseKey = RedisKey.VIEWING_PHASE_KEY + UserContext.getCurrentId();
        String phase = stringRedisTemplate.opsForValue().get(phaseKey);
        if (phase == null){
            stringRedisTemplate.opsForValue().set(phaseKey, "0");
        }
        Integer goldNum = 0;
        String desc = "";
        GoldRecord goldRecord = null;
        for (int i = 0; i < list.size(); i++) {
            if (increment > timeGoldsList.get(i).getTime() && (i + "").equals(phase)){
                if (((list.size() - 1) + "") .equals(stringRedisTemplate.opsForValue().get(phaseKey))){
                    stringRedisTemplate.opsForValue().set(RedisKey.VIEWING_IS_DONE_KEY + UserContext.getCurrentId(), "true");
                }
                stringRedisTemplate.opsForValue().set(RedisKey.VIEWING_PHASE_KEY + UserContext.getCurrentId(), (i + 1) + "");
                goldNum = timeGoldsList.get(i).getGolds();
                desc = "观看"+ (timeGoldsList.get(i).getTime() / 60) +"分钟所得奖励";
                goldRecord = new GoldRecord(idClient.getLongKeyInterval(GOLD_RECORD_TYPE), UserContext.getCurrentId(), goldNum, desc, LocalDate.now());
                goldRecordMapper.insertGoldRecord(goldRecord);
                UserMoney userMoney = userMoneyMapper.selectById(UserContext.getCurrentId());
                userMoney.setGoldNum(userMoney.getGoldNum() + goldNum);
                userMoneyMapper.updateById(userMoney);
                return;
            }
        }
    }
}
